/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.results;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;

import cgl.granules.exception.MarshallingException;
import cgl.granules.util.MarshallingUtil;

/**
 * This class is an implementation of the <code>Results</code> interface.
 * 
 * @author Shrideep Pallickara
 * 
 */

public class ResultsWidget implements Results, Serializable {

    private static final long serialVersionUID = -7134465810850144355L;
    private SequencingInfo sequencingInfo = null;
    private String respondTo = null;
    private String resultIdentifier = null;
    private int resultType = 0;
    private long timestamp = 0;
    private boolean mutable = true;
    private String instanceIdentifier;
    private boolean processingComplete;
    private boolean completeResult;
    private byte[] resultsPayload;
    private ResultsFactory resultsFactory;
    private String moduleName = "ResultsWidget: ";


    protected ResultsWidget(String instanceIdentifier, String resultIdentifier,
            boolean completeResult, boolean processingComplete) {
        this.instanceIdentifier = instanceIdentifier;
        this.resultIdentifier = resultIdentifier;
        this.completeResult = completeResult;
        this.processingComplete = processingComplete;

        timestamp = System.currentTimeMillis();

    }


    @Override
    public SequencingInfo createSequenceInfo() {
        checkForIllegalModification();
        resultsFactory = ResultsFactory.getInstance();
        this.sequencingInfo = resultsFactory.createSequencingInfo();
        return sequencingInfo;
    }


    @Override
    public String getRespondTo() {
        return respondTo;
    }


    @Override
    public void setRespondTo(String respondTo) {
        checkForIllegalModification();
        this.respondTo = respondTo;
    }


    @Override
    public String getResultIdentifier() {
        return resultIdentifier;
    }


    @Override
    public int getResultType() {
        return resultType;
    }


    @Override
    public void setResultType(int resultType) {
        checkForIllegalModification();
        this.resultType = resultType;
    }


    @Override
    public SequencingInfo getSequencingInfo() {
        return sequencingInfo;
    }


    @Override
    public long getTimestamp() {
        return timestamp;
    }


    @Override
    public boolean hasRespondTo() {
        if (respondTo == null) {
            return false;
        }
        return true;
    }


    @Override
    public boolean hasSequencingInfo() {
        if (sequencingInfo == null) {
            return false;
        }
        return true;
    }


    @Override
    public String getInstanceIdentifier() {
        return instanceIdentifier;
    }


    @Override
    public boolean isCompleteResult() {
        return completeResult;
    }


    @Override
    public boolean isMutable() {
        return mutable;
    }


    protected void setImmutable() {
        mutable = false;
        ((SequencingWidget) sequencingInfo).setImmutable();
    }


    @Override
    public boolean isPartialResult() {
        if (completeResult) {
            return false;
        }
        return true;
    }


    @Override
    public boolean isProcessingComplete() {
        return processingComplete;
    }


    @Override
    public byte[] getResultPayload() {
        return resultsPayload;
    }


    @Override
    public void setResultPayload(byte[] resultsPayload) {
        if (resultsPayload == null) {
            throw new NullPointerException("Specified resultsPayload == null");
        }

        checkForIllegalModification();
        this.resultsPayload = resultsPayload;
    }


    @Override
    public byte[] getBytes() throws IOException {
        MarshallingUtil marshaller = MarshallingUtil.getInstance();
        byte[] marshalledBytes = null;
        ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
        DataOutputStream dout =
            new DataOutputStream(new BufferedOutputStream(baOutputStream));

        dout.writeLong(serialVersionUID);
        marshaller.writeString(dout, instanceIdentifier);
        marshaller.writeString(dout, resultIdentifier);

        boolean hasRespondTo = false;
        if (respondTo != null) {
            hasRespondTo = true;
        }
        dout.writeBoolean(hasRespondTo);
        if (hasRespondTo) {
            marshaller.writeString(dout, respondTo);
        }

        dout.writeLong(timestamp);

        mutable = false; /* This should be false by now. */
        dout.writeBoolean(mutable); 
        dout.writeBoolean(completeResult);
        dout.writeBoolean(processingComplete);

        boolean hasSequencingInfo = false;
        if (sequencingInfo != null) {
            hasSequencingInfo = true;
        }
        dout.writeBoolean(hasSequencingInfo);
        if (hasSequencingInfo) {
            byte[] sequencingBytes = sequencingInfo.getBytes();
            dout.writeInt(sequencingBytes.length);
            dout.write(sequencingBytes);
        }

        dout.writeInt(resultType);
        boolean hasResultPayload = false;
        if (resultsPayload != null) {
            hasResultPayload = true;
        }
        dout.writeBoolean(hasResultPayload);
        if (hasResultPayload) {
            dout.writeInt(resultsPayload.length);
            dout.write(resultsPayload);
        }

        dout.flush();
        marshalledBytes = baOutputStream.toByteArray();
        
        baOutputStream.close();
        dout.close();
        return marshalledBytes;
    }


    /**
     * Reconstructs the ResultsWidget from a set of bytes
     * 
     * @param marshalledBytes
     * @throws IOException
     * @throws MarshallingException
     */
    protected ResultsWidget(byte[] marshalledBytes) throws IOException,
        MarshallingException {
        ByteArrayInputStream baInputStream =
            new ByteArrayInputStream(marshalledBytes);
        DataInputStream din =
            new DataInputStream(new BufferedInputStream(baInputStream));
        MarshallingUtil marshaller = MarshallingUtil.getInstance();

        String errorReport = null;
        long resultMarker = din.readLong();
        if (resultMarker != serialVersionUID) {
            errorReport =
                "The result marker " + resultMarker + ", does NOT match the "
                    + "serialVersionUID=" + serialVersionUID;
            throwMarshallingException(errorReport);
        }

        instanceIdentifier = marshaller.readString(din);
        resultIdentifier = marshaller.readString(din);

        boolean hasRespondTo = din.readBoolean();
        if (hasRespondTo) {
            respondTo = marshaller.readString(din);
        }

        timestamp = din.readLong();

        mutable = din.readBoolean();
        if (mutable) {
            mutable = false; /* This should be false. */
        }

        completeResult = din.readBoolean();
        processingComplete = din.readBoolean();

        boolean hasSequencingInfo = din.readBoolean();
        if (hasSequencingInfo) {
            byte[] sequencingBytes = new byte[din.readInt()];
            din.readFully(sequencingBytes);
            resultsFactory = ResultsFactory.getInstance();
            sequencingInfo = resultsFactory.getSequencingInfo(sequencingBytes);
        }

        resultType = din.readInt();

        boolean hasResultPayload = din.readBoolean();
        if (hasResultPayload) {
            resultsPayload = new byte[din.readInt()];
            din.readFully(resultsPayload);
        }

        baInputStream.close();
        din.close();
    }


    private void checkForIllegalModification() {
        if (!mutable) {
            String errorReport = "Results closed for modifications.";
            throw new IllegalModificationException(errorReport);
        }
    }


    /**
     * Throws a MarshallingException with the specified reason
     * 
     * @param errorReport
     * @throws MarshallingException
     */
    private void throwMarshallingException(String errorReport)
        throws MarshallingException {
        throw new MarshallingException(errorReport);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {

    }

}
